package cn.hg.solon.youcan.job.provider;

import static cn.hg.solon.youcan.job.entity.table.SysJobTableDef.SYS_JOB;

import java.util.List;
import java.util.Map;

import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.text.CharSequenceUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.cron.CronUtil;
import org.dromara.hutool.cron.pattern.CronPattern;
import org.dromara.hutool.cron.task.Task;
import org.noear.solon.annotation.Component;

import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryOrderBy;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.solon.service.impl.ServiceImpl;

import cn.hg.solon.youcan.common.constant.AppConstants;
import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.job.constant.JobStatus;
import cn.hg.solon.youcan.job.entity.SysJob;
import cn.hg.solon.youcan.job.kit.AbstractCronJob;
import cn.hg.solon.youcan.job.kit.CronDisallowConcurrentExecution;
import cn.hg.solon.youcan.job.kit.CronJobExecution;
import cn.hg.solon.youcan.job.kit.CronJobKit;
import cn.hg.solon.youcan.job.mapper.SysJobMapper;
import cn.hg.solon.youcan.job.service.SysJobService;

@Component
public class SysJobProvider extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.job.service.SysJobService#checkUnique(cn.hg.solon.youcan.system.entity.SysJob)
     */
    @Override
    public boolean checkUnique(SysJob bean) {
        // 查找已存在记录
        QueryWrapper query = QueryWrapper.create()
            .where(SYS_JOB.GROUP.eq(bean.getGroup())
                .and(SYS_JOB.NAME.eq(bean.getName()))
                .and(SYS_JOB.ID.ne(bean.getId()))
            );

        return ObjUtil.isNull(this.getOne(query));
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.job.service.SysJobService#clear()
     */
    @Override
    public void clear() {
        CronUtil.getScheduler().clear();
        this.stopCron();
    }


    private AbstractCronJob createTask(SysJob job) {
        /*
         * 克隆Job
         */
        SysJob tempJob = BeanUtil.copyProperties(job, SysJob.class);

        if (tempJob.getConcurrent()) {
            return new CronJobExecution(tempJob);
        }

        return new CronDisallowConcurrentExecution(tempJob);
    }


    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.job.provider.SysJobService#init()
     */
    @Override
    public void init() throws ServiceException {
        /*
         * 如果Cron服务没有启动，则启动之
         */
        List<SysJob> jobList = this.list();
        for (SysJob job : jobList) {
            if (CharSequenceUtil.equals(job.getStatus(), JobStatus.OFF)) {
                this.pause(job);
            } else if (CharSequenceUtil.equals(job.getStatus(), JobStatus.ON)) {
                this.resume(job);
            }
        }
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.job.service.SysJobService#selectPageBy(int, int, java.util.Map)
     */
    @Override
    public Page<SysJob> pageByMap(Page<SysJob> page, Map<String, Object> paraMap) {
        String group = (String)paraMap.get("group");
        String word = (String)paraMap.get("word");
        String sortField = (String)paraMap.get("sortField");
        String sortType = (String)paraMap.get("sortType");
        QueryWrapper query = QueryWrapper.create()
            .where(SYS_JOB.GROUP.eq(group).when(StrValidator.isNotBlank(group))
                .and(SYS_JOB.NAME.like(word).when(StrValidator.isNotBlank(word))
                    .or(SYS_JOB.TARGET.like(word).when(StrValidator.isNotBlank(word)))
                    .or(SYS_JOB.CRON.like(word).when(StrValidator.isNotBlank(word)))
                )
            ).orderBy(new QueryOrderBy(new QueryColumn(StrUtil.toUnderlineCase(sortField)), sortType));

        return this.page(page, query);
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.job.service.SysJobService#pause(cn.hg.solon.youcan.system.entity.SysJob, cn.hg.solon.youcan.system.entity.SysUser)
     */
    @Override
    public boolean pause(SysJob job) {
        /*
         * 生成任务Key
         */
        String key = CronJobKit.genTaskKey(job);

        /*
         * 无任务
         */
        if (ObjUtil.isNull(CronUtil.getScheduler().getTask(key))) {
            return true;
        }

        /*
         * 有任务，则移除之
         */
        CronUtil.getScheduler().deschedule(key);

        SysJob exist = this.getById(job.getId());
        if (!StrUtil.equals(exist.getStatus(), JobStatus.OFF)) {
            exist.setStatus(JobStatus.OFF);
            this.update(exist);
        }
        
        return true;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.job.service.SysJobService#resume(cn.hg.solon.youcan.system.entity.SysJob, cn.hg.solon.youcan.system.entity.SysUser)
     */
    @Override
    public boolean resume(SysJob job) {
        /*
         * 生成任务Key
         */
        String key = CronJobKit.genTaskKey(job);

        this.startCron();

        /*
         * 无任务，则创建之
         */
        if (ObjUtil.isNull(CronUtil.getScheduler().getTask(key))) {
            /*
             * 创建Task
             */
            Task task = this.createTask(job);

            CronUtil.getScheduler().schedule(key, job.getCron(), task);
            
            SysJob exist = this.getById(job.getId());
            if (!StrUtil.equals(exist.getStatus(), JobStatus.ON)) {
                exist.setStatus(JobStatus.ON);
                this.update(exist);
            }
        } else {
            /*
             * 有任务，则更新之
             */
            CronUtil.getScheduler().updatePattern(key, CronPattern.of(job.getCron()));
        }

        return true;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.job.service.SysJobService#save(cn.hg.solon.youcan.system.entity.SysJob, cn.hg.solon.youcan.system.entity.SysUser)
     */
    @Override
    public boolean save(SysJob bean) {
        if (!this.checkUnique(bean)) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "任务名称已经存在，请更换其它值！");
        }

        return this.save(bean);
    }


    private void startCron() {
        if (!CronUtil.getScheduler().isStarted()) {
            CronUtil.start();
        }
    }

    private void stopCron() {
        if (CronUtil.getScheduler().isStarted()) {
            CronUtil.stop();
        }
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.job.service.SysJobService#test(cn.hg.solon.youcan.system.entity.SysJob)
     */
    @Override
    public boolean test(SysJob model) {

        /*
         * 创建Task
         */
        Task task = this.createTask(model);

        /*
         * 执行任务（单次）
         */
        try {
            task.execute();
            return true;
        } catch (Throwable e) {
            return false;
        }

    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.job.service.SysJobService#update(cn.hg.solon.youcan.system.entity.SysJob, cn.hg.solon.youcan.system.entity.SysUser)
     */
    @Override
    public boolean update(SysJob bean) {
        if (!this.checkUnique(bean)) {
            throw new ServiceException(AppConstants.RETURN_CODE_VALUE_FAILED, "任务名称已经存在，请更换其它值！");
        }

        return this.updateById(bean);
    }

}
